#region Licence...

//-----------------------------------------------------------------------------
// Date:	17/10/04	Time: 2:33p
// Module:	csscript.cs
// Classes:	CSExecutor
//			ExecuteOptions
//
// This module contains the definition of the CSExecutor class. Which implements
// compiling C# code and executing 'Main' method of compiled assembly
//
// Written by Oleg Shilo (oshilo@gmail.com)
// Copyright (c) 2004-2011. All rights reserved.
//
// Redistribution and use of this code WITHOUT MODIFICATIONS are permitted provided that
// the following conditions are met:
// 1. Redistributions must retain the above copyright notice, this list of conditions
//  and the following disclaimer.
// 2. Neither the name of an author nor the names of the contributors may be used
//	to endorse or promote products derived from this software without specific
//	prior written permission.
//
// Redistribution and use of this code WITH MODIFICATIONS are permitted provided that all
// above conditions are met and software is not used or sold for profit.
//
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//	Caution: Bugs are expected!
//----------------------------------------------

#endregion Licence...

using System;
using System.IO;
using System.Reflection;

#if net1
using System.Collections;
#else

using System.Collections.Generic;

#endif

using System.Text;
using CSScriptLibrary;
using System.Runtime.InteropServices;
using System.Threading;
using System.CodeDom.Compiler;
using System.Windows.Forms;
using System.Globalization;
using System.Diagnostics;
using Microsoft.CSharp;

namespace csscript
{
    internal interface IScriptExecutor
    {
        void ShowHelp();

        void ShowSample();

        ExecuteOptions GetOptions();
    }

    /// <summary>
    /// CSExecutor is an class that implements execution of *.cs files.
    /// </summary>
    class CSExecutor : IScriptExecutor
    {
        #region Public interface...

        /// <summary>
        /// Force caught exceptions to be rethrown.
        /// </summary>
        public bool Rethrow
        {
            get { return rethrow; }
            set { rethrow = value; }
        }

#if net1
        private Settings GetPerisetedSettings(ArrayList appArgs)
#else

        private Settings GetPerisetedSettings(List<string> appArgs)
#endif

        {
            //read persistent settings from configuration file
            Settings settings = null;

            if (options.noConfig)
            {
                if (options.altConfig != "")
                    settings = Settings.Load(Path.GetFullPath(options.altConfig));
            }
            else
                settings = Settings.Load(Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), "css_config.xml"));

            if (settings != null)
            {
                options.hideTemp = settings.HideAutoGeneratedFiles;
                options.altCompiler = settings.ExpandUseAlternativeCompiler();
                options.defaultRefAssemblies = settings.ExpandDefaultRefAssemblies();
                options.postProcessor = settings.ExpandUsePostProcessor();
                options.apartmentState = settings.DefaultApartmentState;
                options.reportDetailedErrorInfo = settings.ReportDetailedErrorInfo;
                options.openEndDirectiveSyntax = settings.OpenEndDirectiveSyntax;
                options.cleanupShellCommand = settings.ExpandCleanupShellCommand();
                options.doCleanupAfterNumberOfRuns = settings.DoCleanupAfterNumberOfRuns;
                options.inMemoryAsm = settings.InMemoryAsssembly;
                //options.useSurrogateHostingProcess = settings.UseSurrogateHostingProcess;
                options.hideCompilerWarnings = settings.HideCompilerWarnings;
                options.TargetFramework = settings.TargetFramework;

                //process default command-line arguments
                string[] defaultCmdArgs = settings.DefaultArguments.Split(" ".ToCharArray());
                defaultCmdArgs = Utils.RemoveEmptyStrings(defaultCmdArgs);

                int firstDefaultScriptArg = CSSUtils.ParseAppArgs(defaultCmdArgs, this);
                if (firstDefaultScriptArg != defaultCmdArgs.Length)
                {
                    options.scriptFileName = defaultCmdArgs[firstDefaultScriptArg];
                    for (int i = firstDefaultScriptArg + 1; i < defaultCmdArgs.Length; i++)
                        if (defaultCmdArgs[i].Trim().Length != 0)
                            appArgs.Add(defaultCmdArgs[i]);
                }

                //if (options.suppressExternalHosting)
                //    options.useSurrogateHostingProcess = settings.UseSurrogateHostingProcess = false;
            }
            return settings;
        }

        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        public void Execute(string[] args, PrintDelegate printDelg, string primaryScript)
        {
            try
            {
                print = printDelg != null ? printDelg : new PrintDelegate(VoidPrint);

                if (args.Length > 0)
                {
                    #region Parse command-line arguments...

                    //Here we need to separate application arguments from script ones.
                    //Script engine arguments are always followed by script arguments
                    //[appArgs][scriptFile][scriptArgs][//x]
#if net1
                    ArrayList appArgs = new ArrayList();
#else
                    List<string> appArgs = new List<string>();
#endif
                    int firstScriptArg = CSSUtils.ParseAppArgs(args, this);
                    if (args.Length <= firstScriptArg)
                    {
                        Environment.ExitCode = 1;
                        return; //no script, no script arguments
                    }

                    //The following will also update corresponding "options" members from "settings" data
                    Settings settings = GetPerisetedSettings(appArgs);

                    //process original command-line arguments
                    if (options.scriptFileName == "")
                    {
                        options.scriptFileName = args[firstScriptArg];
                        firstScriptArg++;
                    }

                    for (int i = firstScriptArg; i < args.Length; i++)
                    {
                        if (args[i].Trim().Length != 0)
                        {
                            if (i == args.Length - 1 && string.Compare(args[args.Length - 1], "//x", true, CultureInfo.InvariantCulture) == 0)
                            {
                                options.startDebugger = true;
                                options.DBG = true;
                            }
                            else
                                appArgs.Add(args[i]);
                        }
                    }
#if net1
                    scriptArgs = (string[])appArgs.ToArray(typeof(string));
#else
                    scriptArgs = appArgs.ToArray();
#endif
                    //searchDirs[0] is the script file directory. Set it only after
                    //the script file resolved because it can be:
                    //	dir defined by the absolute/ralative script file path
                    //	"%CSSCRIPT_DIR%\lib
                    //	settings.SearchDirs
                    //  CacheDir
#if net1
                    ArrayList dirs = new ArrayList();
#else
                    List<string> dirs = new List<string>();
#endif

                    using (IDisposable currDir = new CurrentDirGuard())
                    {
                        if (options.local)
                            Environment.CurrentDirectory = Path.GetDirectoryName(Path.GetFullPath(options.scriptFileName));

                        foreach (string dir in options.searchDirs) //some directories may be already set from command-line
                            dirs.Add(Path.GetFullPath(dir));

                        if (settings != null)
                            foreach (string dir in Environment.ExpandEnvironmentVariables(settings.SearchDirs).Split(",;".ToCharArray()))
                                if (dir.Trim() != "")
                                    dirs.Add(Path.GetFullPath(dir));
                    }

#if net1
                    options.scriptFileName = FileParser.ResolveFile(options.scriptFileName, (string[])dirs.ToArray(typeof(string)));
#else
                    options.scriptFileName = FileParser.ResolveFile(options.scriptFileName, dirs.ToArray());
#endif
                    if (primaryScript != null)
                        options.scriptFileNamePrimary = primaryScript;
                    else
                        options.scriptFileNamePrimary = options.scriptFileName;

                    if (CSExecutor.ScriptCacheDir == "")
                        CSExecutor.SetScriptCacheDir(options.scriptFileName);

                    dirs.Insert(0, Path.GetDirectoryName(Path.GetFullPath(options.scriptFileName)));

                    if (settings != null && settings.HideAutoGeneratedFiles != Settings.HideOptions.DoNotHide)
                        dirs.Add(CSExecutor.ScriptCacheDir);
#if net1
                    options.searchDirs = (string[])dirs.ToArray(typeof(string));
#else
                    options.searchDirs = dirs.ToArray();
#endif
                    CSharpParser.CmdScriptInfo[] cmdScripts = new CSharpParser.CmdScriptInfo[0];

                    //do quick parsing for pre/post scripts, ThreadingModel and embedded script arguments
                    CSharpParser parser = new CSharpParser(options.scriptFileName, true);

                    if (parser.HostOptions.Length != 0)
                    {
                        if (Environment.Version.Major >= 4)
                        {
                            foreach (string optionsSet in parser.HostOptions)
                                foreach (string option in optionsSet.Split(' '))
                                    if (option == "/platform:x86")
                                        options.compilerOptions += " " + option;
                                    else if (option.StartsWith("/version:"))
                                        options.TargetFramework = option.Replace("/version:", "");

                            options.useSurrogateHostingProcess = true;
                        }
                    }
                    else
                    {
#if fork_x86
                        ////x86 process forking only supported for .NET 4.0+
                        ////This is because earlier versions of CLR would require different "platform build" runasm32.exe
                        //if (Environment.Version.Major >= 4)
                        //{
                        //    foreach (string option in parser.CompilerOptions)
                        //        if (option == "/platform:x86" && Environment.Is64BitProcess)
                        //            throw new Surrogate86ProcessRequiredException();

                        //    foreach (string arg in Utils.Concat(args, parser.Args))
                        //        if (arg.StartsWith("/co:"))
                        //            foreach (string option in arg.Split('/'))
                        //                if (option == "platform:x86" && Environment.Is64BitProcess)
                        //                    throw new Surrogate86ProcessRequiredException();
                        //}
#endif
                    }

                    //analyse ThreadingModel to use it whith execution thread
                    if (File.Exists(options.scriptFileName))
                    {
                        if (parser.ThreadingModel != ApartmentState.Unknown)
                            options.apartmentState = parser.ThreadingModel;
#if net1

                        ArrayList preScripts = new ArrayList(parser.CmdScripts);
                        foreach (CSharpParser.ImportInfo info in parser.Imports)
                        {
                            try
                            {
                                string file = FileParser.ResolveFile(info.file, options.searchDirs);
                                if (file.IndexOf(".g.cs") == -1) //non auto-generated file
                                    preScripts.AddRange(new CSharpParser(file, true).CmdScripts);
                            }
                            catch { } //some files may not be generated yet
                        }

                        cmdScripts = (CSharpParser.CmdScriptInfo[])preScripts.ToArray(typeof(CSharpParser.CmdScriptInfo));
#else
                        List<string> newSearchDirs = new List<string>(options.searchDirs);

                        using (IDisposable currDir = new CurrentDirGuard())
                        {
                            Environment.CurrentDirectory = Path.GetDirectoryName(Path.GetFullPath(options.scriptFileName));

                            foreach (string dir in parser.ExtraSearchDirs)
                                newSearchDirs.Add(Path.GetFullPath(dir));

                            options.searchDirs = newSearchDirs.ToArray();
                        }

                        List<CSharpParser.CmdScriptInfo> preScripts = new List<CSharpParser.CmdScriptInfo>(parser.CmdScripts);
                        foreach (CSharpParser.ImportInfo info in parser.Imports)
                        {
                            try
                            {
                                string file = FileParser.ResolveFile(info.file, options.searchDirs);
                                if (file.IndexOf(".g.cs") == -1) //non auto-generated file
                                {
                                    using (IDisposable currDir = new CurrentDirGuard())
                                    {
                                        CSharpParser impParser = new CSharpParser(file, true, options.searchDirs);
                                        Environment.CurrentDirectory = Path.GetDirectoryName(file);

                                        foreach (string dir in impParser.ExtraSearchDirs)
                                            newSearchDirs.Add(Path.GetFullPath(dir));

                                        options.searchDirs = newSearchDirs.ToArray();
                                    }
                                    preScripts.AddRange(new CSharpParser(file, true).CmdScripts);
                                }
                            }
                            catch { } //some files may not be generated yet
                        }

                        cmdScripts = preScripts.ToArray();
#endif
                        if (primaryScript == null)//this is a primary script
                        {
                            int firstEmbeddedScriptArg = CSSUtils.ParseAppArgs(parser.Args, this);
                            if (firstEmbeddedScriptArg != -1)
                            {
                                for (int i = firstEmbeddedScriptArg; i < parser.Args.Length; i++)
                                    appArgs.Add(parser.Args[i]);
                            }
#if net1
                            scriptArgs = (string[])appArgs.ToArray(typeof(string));
#else
                            scriptArgs = appArgs.ToArray();
#endif
                        }
                    }

                    #endregion Parse command-line arguments...

                    ExecuteOptions originalOptions = (ExecuteOptions)options.Clone(); //preserve master script options
                    string originalCurrDir = Environment.CurrentDirectory;

                    //run prescripts
                    //Note: during the secondary script execution static options will be modified (this is required for
                    //browsing in CSSEnvironment with reflection). So reset it back with originalOptions after the execution is completed
                    foreach (CSharpParser.CmdScriptInfo info in cmdScripts)
                        if (info.preScript)
                        {
                            Environment.CurrentDirectory = originalCurrDir;
                            info.args[1] = FileParser.ResolveFile(info.args[1], originalOptions.searchDirs);

                            CSExecutor exec = new CSExecutor(info.abortOnError, originalOptions);

                            if (originalOptions.DBG)
                            {
#if net1
                                ArrayList newArgs = new ArrayList();
                                newArgs.AddRange(info.args);
                                newArgs.Insert(0, "/dbg");
                                info.args = (string[])newArgs.ToArray(typeof(string));
#else
                                List<string> newArgs = new List<string>();
                                newArgs.AddRange(info.args);
                                newArgs.Insert(0, "/dbg");
                                info.args = newArgs.ToArray();
#endif
                            }
                            if (originalOptions.verbose)
                            {
#if net1
                                ArrayList newArgs = new ArrayList();
                                newArgs.AddRange(info.args);
                                newArgs.Insert(0, "/verbose");
                                info.args = (string[])newArgs.ToArray(typeof(string));
#else
                                List<string> newArgs = new List<string>();
                                newArgs.AddRange(info.args);
                                newArgs.Insert(0, "/verbose");
                                info.args = newArgs.ToArray();
#endif
                            }
                            if (info.abortOnError)
                                exec.Execute(info.args, printDelg, originalOptions.scriptFileName);
                            else
                                exec.Execute(info.args, null, originalOptions.scriptFileName);
                        }

                    options = originalOptions;
                    ExecuteOptions.options = originalOptions; //update static members as well
                    Environment.CurrentDirectory = originalCurrDir;

                    //Run main script
                    //We need to start the execution in a new thread as it is the only way
                    //to set desired ApartmentState under .NET 2.0
                    Thread newThread = new Thread(new ThreadStart(this.ExecuteImpl));
#if net1
                    newThread.ApartmentState = options.apartmentState;
#else
                    newThread.SetApartmentState(options.apartmentState);
#endif
                    newThread.Start();
                    newThread.Join();
                    if (lastException != null)
                        if (lastException is SurrogateHostProcessRequiredException)
                            throw lastException;
                        else
                            throw new ApplicationException("Script " + options.scriptFileName + " cannot be executed.", lastException);

                    //run postscripts
                    foreach (CSharpParser.CmdScriptInfo info in cmdScripts)
                        if (!info.preScript)
                        {
                            Environment.CurrentDirectory = originalCurrDir;
                            info.args[1] = FileParser.ResolveFile(info.args[1], originalOptions.searchDirs);

                            CSExecutor exec = new CSExecutor(info.abortOnError, originalOptions);

                            if (originalOptions.DBG)
                            {
#if net1
                                ArrayList newArgs = new ArrayList();
                                newArgs.AddRange(info.args);
                                newArgs.Insert(0, "/dbg");
                                info.args = (string[])newArgs.ToArray(typeof(string));
#else

                                List<string> newArgs = new List<string>();
                                newArgs.AddRange(info.args);
                                newArgs.Insert(0, "/dbg");
                                info.args = newArgs.ToArray();
#endif
                            }
                            if (originalOptions.verbose)
                            {
#if net1
                                ArrayList newArgs = new ArrayList();
                                newArgs.AddRange(info.args);
                                newArgs.Insert(0, "/verbose");
                                info.args = (string[])newArgs.ToArray(typeof(string));
#else

                                List<string> newArgs = new List<string>();
                                newArgs.AddRange(info.args);
                                newArgs.Insert(0, "/verbose");
                                info.args = newArgs.ToArray();
#endif
                            }
                            if (info.abortOnError)
                            {
                                exec.Rethrow = true;
                                exec.Execute(info.args, printDelg, originalOptions.scriptFileName);
                            }
                            else
                                exec.Execute(info.args, null, originalOptions.scriptFileName);
                        }
                }
                else
                {
                    ShowHelp();
                }
            }
            catch (Surrogate86ProcessRequiredException)
            {
                throw;
            }
            catch (SurrogateHostProcessRequiredException)
            {
                throw;
            }
            catch (Exception e)
            {
                Exception ex = e;
                if (e is System.Reflection.TargetInvocationException)
                    ex = e.InnerException;

                if (rethrow)
                {
                    throw ex;
                }
                else
                {
                    Environment.ExitCode = 1;
                    if (options.reportDetailedErrorInfo)
                        print(ex.ToString());
                    else
                        print(ex.Message); //Mono friendly
                }
            }
        }

        /// <summary>
        /// Returns custom application config file.
        /// </summary>
        internal string GetCustomAppConfig(string[] args)
        {
            try
            {
                if (args.Length > 0)
                {
                    int firstScriptArg = CSSUtils.ParseAppArgs(args, this);
                    if (args.Length > firstScriptArg)
                    {
                        Settings settings = null;
                        if (options.noConfig)
                        {
                            if (options.altConfig != "")
                                settings = Settings.Load(Path.GetFullPath(options.altConfig)); //read persistent settings from configuration file
                        }
                        else
                            settings = Settings.Load(Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), "css_config.xml"));

                        if (!options.useScriptConfig && (settings == null || settings.DefaultArguments.IndexOf("/sconfig") == -1))
                            return "";

                        string script = args[firstScriptArg];
#if net1
                        ArrayList dirs = new ArrayList();
#else
                        List<string> dirs = new List<string>();
#endif
                        string libDir = Environment.ExpandEnvironmentVariables(@"%CSSCRIPT_DIR%\lib");
                        if (!libDir.StartsWith("%"))
                            dirs.Add(libDir);

                        if (settings != null)
                            dirs.AddRange(Environment.ExpandEnvironmentVariables(settings.SearchDirs).Split(",;".ToCharArray()));

#if net1
                        string[] searchDirs = (string[])dirs.ToArray(typeof(string));
#else
                        string[] searchDirs = dirs.ToArray();
#endif
                        script = FileParser.ResolveFile(script, searchDirs);
                        if (options.customConfigFileName != "")
                            return Path.Combine(Path.GetDirectoryName(script), options.customConfigFileName);
                        if (File.Exists(script + ".config"))
                            return script + ".config";
                        else if (File.Exists(Path.ChangeExtension(script, ".exe.config")))
                            return Path.ChangeExtension(script, ".exe.config");
                    }
                }
            }
            catch
            {
                //ignore the exception because it will be raised (again) and handeled by the Execute method
            }
            return "";
        }

        /// <summary>
        /// Dummy 'print' to suppress displaying application messages.
        /// </summary>
        static void VoidPrint(string msg)
        {
        }

        /// <summary>
        /// This method implements compiling and execution of the script.
        /// </summary>
        public Exception lastException;

        /// <summary>
        /// This method implements compiling and execution of the script.
        /// </summary>
        private void ExecuteImpl()
        {
            try
            {
                //System.Diagnostics.Debug.Assert(false);
                if (options.processFile)
                {
                    if (options.local)
                        Environment.CurrentDirectory = Path.GetDirectoryName(Path.GetFullPath(options.scriptFileName));

                    if (!options.noLogo)
                    {
                        Console.WriteLine(AppInfo.appLogo);
                    }

                    if (Environment.GetEnvironmentVariable("EntryScript") == null)
                        Environment.SetEnvironmentVariable("EntryScript", options.scriptFileName);

                    if (options.verbose)
                    {
                        Console.WriteLine("> ----------------");
                        Console.WriteLine("  TragetFramework: " + options.TargetFramework); 
                        Console.WriteLine("  CurrentDirectory: " + Environment.CurrentDirectory);
                        Console.WriteLine("  Executing: " + Path.GetFullPath(options.scriptFileName));
                        Console.WriteLine("  Arguments: ");
                        for (int i = 0; i < scriptArgs.Length; i++)
                            Console.WriteLine("    " + i + " - " + scriptArgs[i]);
                        Console.WriteLine("  SearchDirectories: ");
                        for (int i = 0; i < options.searchDirs.Length; i++)
                            Console.WriteLine("    " + i + " - " + options.searchDirs[i]);
                        Console.WriteLine("> ----------------");
                        Console.WriteLine("");
                    }

                    bool fileUnlocked = false;
                    using (Mutex fileLock = new Mutex(false, "Process." + options.scriptFileName.GetHashCode().ToString()))
                        try
                        {
                            int start = Environment.TickCount;
                            fileLock.WaitOne(3000, false); //let other thread/process (if any) to finish loading/compiling the same file; 3 seconds should be enough, if you need more use more sophisticated synchronization
                            Trace.WriteLine(">>>  Waited  " + (Environment.TickCount - start));

                            //compile
                            string assemblyFileName = options.useCompiled ? GetAvailableAssembly(options.scriptFileName) : null;

                            if (options.useCompiled && options.useSmartCaching)
                            {
                                if (assemblyFileName != null)
                                {
                                    if (MetaDataItems.IsOutOfDate(options.scriptFileName, assemblyFileName))
                                    {
                                        assemblyFileName = null;
                                    }
                                }
                            }

                            if (options.forceCompile && assemblyFileName != null)
                            {
                                File.Delete(assemblyFileName);
                                assemblyFileName = null;
                            }

                            //add searchDirs to PATH to support search path for native dlls
                            //need to do this before copmpilation or execution
                            string path = Environment.GetEnvironmentVariable("PATH");
                            foreach (string s in options.searchDirs)
                                path += ";" + s;
#if net1
                            SetEnvironmentVariable("PATH", path);
#else
                            Environment.SetEnvironmentVariable("PATH", path);
#endif
                            //it is possible thet there are fully compiled/cached and up to date script bu no host compiled yet
                            string host = ScriptLauncherBuilder.GetLauncherName(assemblyFileName);
                            bool surrogateHostMissing = (options.useSurrogateHostingProcess &&
                                (!File.Exists(host) || !CSSUtils.HaveSameTimestamp(host, assemblyFileName)));

                            if (options.buildExecutable || !options.useCompiled || (options.useCompiled && assemblyFileName == null) || options.forceCompile || surrogateHostMissing)
                            {
                                try
                                {
                                    assemblyFileName = Compile(options.scriptFileName);
                                }
                                catch
                                {
                                    print("Error: Specified file could not be compiled.\n");
                                    throw;
                                }
                                finally
                                {
                                    try { fileLock.ReleaseMutex(); }
                                    catch { }
                                    fileUnlocked = true;
                                }
                            }

                            //execute
                            if (!options.supressExecution)
                            {
                                try
                                {
                                    if (options.useSurrogateHostingProcess)
                                    {
                                        throw new SurrogateHostProcessRequiredException(assemblyFileName, scriptArgs, options.startDebugger);
                                    }

                                    if (options.startDebugger)
                                    {
                                        System.Diagnostics.Debugger.Launch();
                                        if (System.Diagnostics.Debugger.IsAttached)
                                            System.Diagnostics.Debugger.Break();
                                    }

                                    if (options.useCompiled || options.cleanupShellCommand != "")
                                    {
                                        AssemblyResolver.CacheProbingResults = true; //it is reasonable safe to do the agressive probing as we are executing only a single script (standalone execution not a script hosting model)

                                        //despite the name of the class the execution (assembly loading) will be in the current domain
                                        //I am just reusing some functionality of the RemoteExecutor class.
                                        RemoteExecutor executor = new RemoteExecutor(options.searchDirs);
                                        executor.ExecuteAssembly(assemblyFileName, scriptArgs);
                                    }
                                    else
                                    {
                                        //Load and execute assembly in a different domain to make it possible to unload assembly before clean up
                                        AssemblyExecutor executor = new AssemblyExecutor(assemblyFileName, "AsmExecution");
                                        executor.Execute(scriptArgs);
                                    }
                                }
                                catch (SurrogateHostProcessRequiredException)
                                {
                                    throw;
                                }
                                catch
                                {
                                    print("Error: Specified file could not be executed.\n");
                                    throw;
                                }

                                //cleanup
                                if (File.Exists(assemblyFileName) && !options.useCompiled && options.cleanupShellCommand == "")
                                {
                                    try
                                    {
                                        File.Delete(assemblyFileName);
                                    }
                                    catch { }
                                }

                                if (options.cleanupShellCommand != "")
                                {
                                    string counterFile = Path.Combine(GetScriptTempDir(), "counter.txt");
                                    int prevRuns = 0;
                                    try
                                    {
                                        using (StreamReader sr = new StreamReader(counterFile))
                                        {
                                            prevRuns = int.Parse(sr.ReadToEnd());
                                        }
                                    }
                                    catch { }

                                    if (prevRuns > options.doCleanupAfterNumberOfRuns)
                                    {
                                        prevRuns = 1;
                                        string[] cmd = options.ExtractShellCommand(options.cleanupShellCommand);
                                        if (cmd.Length > 1)
                                            Process.Start(cmd[0], cmd[1]);
                                        else
                                            Process.Start(cmd[0]);
                                    }
                                    else
                                        prevRuns++;

                                    try
                                    {
                                        using (StreamWriter sw = new StreamWriter(counterFile))
                                            sw.Write(prevRuns);
                                    }
                                    catch { }
                                }
                            }
                        }
                        finally
                        {
                            try { if (!fileUnlocked) fileLock.ReleaseMutex(); } //using fileUnlocked to avoid throwing unnecessary exception
                            catch { }
                        }
                }
            }
            catch (Exception e)
            {
                Exception ex = e;
                if (e is System.Reflection.TargetInvocationException)
                    ex = e.InnerException;

                if (rethrow || e is SurrogateHostProcessRequiredException)
                {
                    lastException = ex;
                }
                else
                {
                    Environment.ExitCode = 1;
                    if (options.reportDetailedErrorInfo)
                        print(ex.ToString());
                    else
                        print(ex.Message); //Mono friendly
                }
            }
        }

        /// <summary>
        /// Compiles C# script file into assembly.
        /// </summary>
        public string Compile(string scriptFile, string assemblyFile, bool debugBuild)
        {
            if (assemblyFile != null)
                options.forceOutputAssembly = assemblyFile;
            else
            {
                string cacheFile = Path.Combine(CSExecutor.GetCacheDirectory(scriptFile), Path.GetFileNameWithoutExtension(scriptFile) + ".csc");
                options.forceOutputAssembly = cacheFile;
            }
            if (debugBuild)
                options.DBG = true;
            return Compile(scriptFile);
        }

        #endregion Public interface...

        #region Class data...

        /// <summary>
        /// C# Script arguments array (sub array of application arguments array).
        /// </summary>
        string[] scriptArgs;
        /// <summary>
        /// Callback to print application messages to appropriate output.
        /// </summary>
        static PrintDelegate print;
        /// <summary>
        /// Container for paresed command line parguments
        /// </summary>
        static internal ExecuteOptions options = new ExecuteOptions();
        /// <summary>
        /// Flag to force to rethrow critical exceptions
        /// </summary>
        bool rethrow;

        #endregion Class data...

        #region Class methods...

        /// <summary>
        /// Constructor
        /// </summary>
        public CSExecutor()
        {
            rethrow = false;
            options = new ExecuteOptions();
        }

        /// <summary>
        /// Constructor
        /// </summary>
        public CSExecutor(bool rethrow, ExecuteOptions optionsBase)
        {
            this.rethrow = rethrow;
            options = new ExecuteOptions();
            //force to read all relative options data from the config file
            options.noConfig = optionsBase.noConfig;
            options.altConfig = optionsBase.altConfig;
        }

        public ExecuteOptions GetOptions()
        {
            return options;
        }

        /// <summary>
        /// Checks/returns if compiled C# script file (ScriptName + "c") available and valid.
        /// </summary>
        internal string GetAvailableAssembly(string scripFileName)
        {
            string retval = null;
            string asmFileName = options.hideTemp != Settings.HideOptions.DoNotHide ? Path.Combine(CSExecutor.ScriptCacheDir, Path.GetFileName(scripFileName) + "c") : scripFileName + "c";
            if (File.Exists(asmFileName) && File.Exists(scripFileName))
            {
                FileInfo scriptFile = new FileInfo(scripFileName);
                FileInfo asmFile = new FileInfo(asmFileName);

                if (asmFile.LastWriteTime == scriptFile.LastWriteTime &&
                    asmFile.LastWriteTimeUtc == scriptFile.LastWriteTimeUtc)
                {
                    retval = asmFileName;
                }
            }
            return retval;
        }

        class UniqueAssemblyLocations
        {
            public static explicit operator string[](UniqueAssemblyLocations obj)
            {
                string[] retval = new string[obj.locations.Count];
                obj.locations.Values.CopyTo(retval, 0);
                return retval;
            }

            public void AddAssembly(string location)
            {
                string assemblyID = Path.GetFileName(location);
                if (!locations.ContainsKey(assemblyID))
                    locations[assemblyID] = location;
            }

            System.Collections.Hashtable locations = new System.Collections.Hashtable();
        }

        ICodeCompiler LoadCompiler(string scriptFileName, ref string[] filesToInject)
        {
            ICodeCompiler compiler;
            if (options.altCompiler == "")
            {
                //#pragma warning disable 618
#if net1
                compiler = new CSharpCodeProvider().CreateCompiler();
                if (options.InjectScriptAssemblyAttribute && Assembly.GetExecutingAssembly().FullName.IndexOf("CSScriptLibrary") != -1) //"We are" CSScriptLibrary assembly
                    filesToInject = Utils.Concat(filesToInject, CSSUtils.GetScriptedCodeAttributeInjectionCode());

#else
                //#pragma warning disable 618
                IDictionary<string, string> providerOptions = new Dictionary<string, string>();
                providerOptions["CompilerVersion"] = options.TargetFramework;
                compiler = new CSharpCodeProvider(providerOptions).CreateCompiler();

                if (options.InjectScriptAssemblyAttribute && Assembly.GetExecutingAssembly().FullName.Contains("CSScriptLibrary")) //"We are" CSScriptLibrary assembly
                    filesToInject = Utils.Concat(filesToInject, CSSUtils.GetScriptedCodeAttributeInjectionCode());

                //#pragma warning restore 618
#endif
                //#pragma warning restore 618
            }
            else
            {
                try
                {
                    Assembly asm;
                    if (Path.IsPathRooted(options.altCompiler))
                    {
                        //absolut path
                        asm = Assembly.LoadFrom(options.altCompiler);
                    }
                    else
                    {
                        //look in the following folders
                        // 1. Executable location
                        // 2. Executable location + "Lib"
                        // 3. CSScriptLibrary.dll location
                        string probingDir = Path.GetDirectoryName(Application.ExecutablePath);
                        string altCompilerFile = Path.Combine(probingDir, options.altCompiler);
                        if (File.Exists(altCompilerFile))
                        {
                            asm = Assembly.LoadFrom(altCompilerFile);
                        }
                        else
                        {
                            probingDir = Path.Combine(probingDir, "Lib");
                            altCompilerFile = Path.Combine(probingDir, options.altCompiler);
                            if (File.Exists(altCompilerFile))
                            {
                                asm = Assembly.LoadFrom(altCompilerFile);
                            }
                            else
                            {
                                //in case of CSScriptLibrary.dll "this" is not defined in the main executable
                                probingDir = Path.GetDirectoryName(this.GetType().Assembly.Location);
                                altCompilerFile = Path.Combine(probingDir, options.altCompiler);
                                if (File.Exists(altCompilerFile))
                                {
                                    asm = Assembly.LoadFrom(Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), options.altCompiler));
                                }
                                else
                                    throw new ApplicationException("Cannot find alternative compiler \"" + options.altCompiler + "\"");
                            }
                        }
                    }
                    Type[] types = asm.GetModules()[0].FindTypes(Module.FilterTypeName, "CSSCodeProvider");

#if net1
                    MethodInfo method = types[0].GetMethod("CreateCompiler");
                    compiler = (ICodeCompiler)method.Invoke(null, new object[] { scriptFileName });  //the script file name may influence what compiler will be created (e.g. *.vb vs. *.cs)
#else
                    MethodInfo method = types[0].GetMethod("CreateCompilerVersion");
                    if (method != null)
                    {
                        compiler = (ICodeCompiler)method.Invoke(null, new object[] { scriptFileName, options.TargetFramework });  //the script file name may influence what compiler will be created (e.g. *.vb vs. *.cs)
                    }
                    else
                    {
                        method = types[0].GetMethod("CreateCompiler");
                        compiler = (ICodeCompiler)method.Invoke(null, new object[] { scriptFileName });  //the script file name may influence what compiler will be created (e.g. *.vb vs. *.cs)
                    }
#endif
                }
                catch (Exception ex)
                {
                    throw new ApplicationException("Cannot use alternative compiler (" + options.altCompiler + "). You may want to adjust 'CSSCRIPT_DIR' environment variable or disable alternative compiler by setting 'useAlternativeCompiler' to empty value in the css_config.xml file.", ex);
                }
            }
            return compiler;
        }

        void AddReferencedAssemblies(CompilerParameters compilerParams, string scriptFileName, ScriptParser parser)
        {
            UniqueAssemblyLocations requestedRefAsms = new UniqueAssemblyLocations();

#if net1
            ArrayList refAssemblies = new ArrayList();
#else
            List<string> refAssemblies = new List<string>();
#endif
            if (options.shareHostRefAssemblies)
                foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies())
                {
                    try
                    {
                        if (asm.FullName.StartsWith("Anonymously Hosted DynamicMethods Assembly"))
                            continue;

                        if (!File.Exists(asm.Location) || asm.Location.Contains("mscorlib"))
                            continue;
#if net4
                        if (asm.Location.Contains("mscorlib")) //CLR 4.0 adds mscorlib automatically
                            continue;
#endif
                        requestedRefAsms.AddAssembly(asm.Location);
                    }
                    catch
                    {
                        //Under ASP.NET some assemblies do not have location (e.g. dynamically built/emitted assemblies)
                        //in such case NotSupportedException will be raised

                        //In fact ignore all exceptions at we should continue if for what ever reason assembly location cannot be obtained
                    }
                }

            //add assemblies referenced from command line
            string[] cmdLineAsms = options.refAssemblies;
            if (!options.useSurrogateHostingProcess)
            {
                string[] defaultAsms = options.defaultRefAssemblies.Replace(" ", "").Split(";,".ToCharArray());
                
                foreach (string asmName in Utils.Concat(defaultAsms, cmdLineAsms))
                {
                    if (asmName == "")
                        continue;

                    string nameSpace = asmName;
                    if (asmName.ToLower().EndsWith(".dll"))
                        nameSpace = asmName.Substring(0, asmName.Length - 4);

                    foreach (string asm in AssemblyResolver.FindAssembly(nameSpace, options.searchDirs))
                        requestedRefAsms.AddAssembly(NormalizeGacAssemblyPath(asm));
                }
            }

            AssemblyResolver.ignoreFileName = Path.GetFileNameWithoutExtension(scriptFileName) + ".dll";

            //add local and global assemblies (if found) that have the same assembly name as a namespace
            foreach (string nmSpace in parser.ReferencedNamespaces)
            {
                bool ignore = false; //user may nominate namespaces to be excluded fro namespace-to-asm rosolving
                foreach (string ignoreNamespace in parser.IgnoreNamespaces)
                    if (ignoreNamespace == nmSpace)
                        ignore = true;

                if (!ignore)
                    foreach (string asm in AssemblyResolver.FindAssembly(nmSpace, options.searchDirs))
                        requestedRefAsms.AddAssembly(NormalizeGacAssemblyPath(asm));
            }
            //add assemblies referenced from code
            foreach (string asmName in parser.ReferencedAssemblies)
                if (asmName.StartsWith("\"") && asmName.EndsWith("\"")) //absolute path
                {
                    //not-searchable assemblies
                    string asm = asmName.Replace("\"", "");
                    requestedRefAsms.AddAssembly(NormalizeGacAssemblyPath(asm));
                }
                else
                {
                    string nameSpace = asmName;
                    if (asmName.ToLower().EndsWith(".dll"))
                        nameSpace = asmName.Substring(0, asmName.Length - 4);

                    string[] files = AssemblyResolver.FindAssembly(nameSpace, options.searchDirs);
                    if (files.Length > 0)
                        foreach (string asm in files)
                            requestedRefAsms.AddAssembly(NormalizeGacAssemblyPath(asm));
                    else
                        requestedRefAsms.AddAssembly(nameSpace + ".dll");
                }

            compilerParams.ReferencedAssemblies.AddRange((string[])requestedRefAsms);
        }

        string NormalizeGacAssemblyPath(string asm)
        {
            //e.g. v3.5
            string currentFramework = string.Format("v{0}.{1}", Environment.Version.Major, Environment.Version.MajorRevision);
            if (options.useSurrogateHostingProcess && options.TargetFramework != currentFramework)
            {
                if (asm.IndexOf("\\GAC_MSIL\\") != -1) //GAC assembly
                    return Path.GetFileName(asm);
                else
                    return asm;
            }
            else 
                return asm;
        }
        /// <summary>
        /// Compiles C# script file.
        /// </summary>
        private string Compile(string scriptFileName)
        {
            //System.Diagnostics.Debug.Assert(false);
            bool generateExe = options.buildExecutable;
            string scriptDir = Path.GetDirectoryName(scriptFileName);
            string assemblyFileName = "";

            //options may be uninitialized in case we are compiling from CSScriptLibrary
            if (options.searchDirs.Length == 0)
                options.searchDirs = new string[] { scriptDir };

            //parse source file in order to find all referenced assemblies
            //ASSUMPTION: assembly name is the same as namespace + ".dll"
            //if script doesn't follow this assumption user will need to
            //specify assemblies explicitly
            ScriptParser parser = new ScriptParser(scriptFileName, options.searchDirs);
            options.searchDirs = parser.SearchDirs; //parser.searchDirs may be updated as result of script parsing

            string[] filesToInject = new string[0];

            ICodeCompiler compiler = LoadCompiler(scriptFileName, ref filesToInject);

            CompilerParameters compilerParams = new CompilerParameters();

            foreach (string option in parser.CompilerOptions)
                Utils.AddCompilerOptions(compilerParams, option);

            if (options.DBG)
                Utils.AddCompilerOptions(compilerParams, "/d:DEBUG /d:TRACE");

            if (options.compilerOptions != string.Empty)
                Utils.AddCompilerOptions(compilerParams, options.compilerOptions);

            compilerParams.IncludeDebugInformation = options.DBG;
            compilerParams.GenerateExecutable = generateExe;
            //compileParams.GenerateExecutable = true;
            compilerParams.GenerateInMemory = false;

            AddReferencedAssemblies(compilerParams, scriptFileName, parser);

            //add resources referenced from code
            foreach (string resFile in parser.ReferencedResources)
            {
                string file = null;
                foreach (string dir in options.searchDirs)
                {
                    file = Path.IsPathRooted(resFile) ? Path.GetFullPath(resFile) : Path.Combine(dir, resFile);
                    if (File.Exists(file))
                        break;
                }

                if (file == null)
                    file = resFile;

                Utils.AddCompilerOptions(compilerParams, "\"/res:" + file + "\""); //e.g. /res:C:\\Scripting.Form1.resources";
            }

            if (options.forceOutputAssembly != "")
            {
                assemblyFileName = options.forceOutputAssembly;
            }
            else
            {
                if (generateExe)
                    assemblyFileName = Path.Combine(scriptDir, Path.GetFileNameWithoutExtension(scriptFileName) + ".exe");
                else if (options.useCompiled || options.DLLExtension)
                {
                    if (options.DLLExtension)
                        assemblyFileName = Path.Combine(scriptDir, Path.GetFileNameWithoutExtension(scriptFileName) + ".dll");
                    else if (options.hideTemp != Settings.HideOptions.DoNotHide)
                        assemblyFileName = Path.Combine(CSExecutor.ScriptCacheDir, Path.GetFileName(scriptFileName) + "c");
                    else
                        assemblyFileName = scriptFileName + "c";
                }
                else
                {
                    string tempFile = GetScriptTempFile();
                    assemblyFileName = Path.ChangeExtension(tempFile, ".dll");
                }
            }

            if (generateExe && options.buildWinExecutable)
                Utils.AddCompilerOptions(compilerParams, "/target:winexe");

            if (File.Exists(assemblyFileName))
                File.Delete(assemblyFileName);

            compilerParams.OutputAssembly = assemblyFileName;

            string[] filesToCompile = new string[parser.FilesToCompile.Length];

            Array.Copy(parser.FilesToCompile, filesToCompile, parser.FilesToCompile.Length);

            if (options.autoClass)
                filesToCompile[0] = CSSUtils.GenerateAutoclass(filesToCompile[0]);

            CompilerResults results;
            if (generateExe)
            {
                results = CompileAssembly(compiler, compilerParams, filesToCompile);
            }
            else
            {
                if (filesToInject.Length != 0)
                {
                    filesToCompile = Utils.Concat(filesToCompile, filesToInject);
                }

                string originalExtension = Path.GetExtension(compilerParams.OutputAssembly);
                if (originalExtension != ".dll")
                {
                    //Despite the usage of .dll file name is not required for MS C# compiler we need to do this because
                    //some compilers (Mono, VB) accept only dll or exe file extensions.
                    compilerParams.OutputAssembly = Path.ChangeExtension(compilerParams.OutputAssembly, ".dll");

                    if (File.Exists(compilerParams.OutputAssembly))
                        File.Delete(compilerParams.OutputAssembly);

                    results = CompileAssembly(compiler, compilerParams, filesToCompile);

                    if (File.Exists(compilerParams.OutputAssembly))
                    {
                        int attempts = 0;
                        while (true)
                        {
                            //There were reports of MS C# compiler (csc.exe) not releasing OutputAssembly file
                            //after compilation finished. Thus wait a little...
                            //BTW. on Mono 1.2.4 it happens all the time
                            try
                            {
                                attempts++;

                                File.Move(compilerParams.OutputAssembly, Path.ChangeExtension(compilerParams.OutputAssembly, originalExtension));

                                break;
                            }
                            catch
                            {
                                if (attempts > 2)
                                {
                                    //yep we can get here as Mono 1.2.4 on Windows never ever releases the assembly
                                    File.Copy(compilerParams.OutputAssembly, Path.ChangeExtension(compilerParams.OutputAssembly, originalExtension), true);
                                    break;
                                }
                                else
                                    Thread.Sleep(100);
                            }
                        }
                    }
                }
                else
                {
                    if (File.Exists(compilerParams.OutputAssembly))
                        File.Delete(compilerParams.OutputAssembly);
                    results = CompileAssembly(compiler, compilerParams, filesToCompile);
                }
            }

            ProcessCompilingResult(results, compilerParams, parser, scriptFileName, assemblyFileName);

            if (options.useSurrogateHostingProcess && !options.supressExecution)
            {
                new ScriptLauncherBuilder().BuildSurrogateLauncher(assemblyFileName, options.TargetFramework, compilerParams, options.apartmentState);
            }

            return assemblyFileName;
        }

        CompilerResults CompileAssembly(ICodeCompiler compiler, CompilerParameters compilerParams, string[] filesToCompile)
        {
            //Debug.Assert(false);
            //foreach (string file in compilerParams.ReferencedAssemblies)
            //    if(file.Contains("mscorlib"))
            //        Trace.WriteLine(file);

            CompilerResults retval = compiler.CompileAssemblyFromFileBatch(compilerParams, filesToCompile);

            if (!retval.Errors.HasErrors && options.postProcessor != "")
            {
                string rawAssembly = compilerParams.OutputAssembly + ".raw";
                try
                {
                    MethodInfo postProcessor = Assembly.LoadFrom(options.postProcessor)
                                                       .GetType("CSSPostProcessor", true)
                                                       .GetMethod("Process");

                    string[] refAsms = new string[compilerParams.ReferencedAssemblies.Count];
                    compilerParams.ReferencedAssemblies.CopyTo(refAsms, 0);

                    postProcessor.Invoke(null, new object[] {
                                            compilerParams.OutputAssembly,
                                            refAsms,
                                            options.searchDirs
                                            });
                }
                catch (Exception e)
                {
                    throw new ApplicationException("Cannon post-process compiled script (set UsePostProcessor to \"null\" if the problem persist).\n" + e.Message);
                }
            }

            return retval;
        }

        void ProcessCompilingResult(CompilerResults results, CompilerParameters compilerParams, ScriptParser parser, string scriptFileName, string assemblyFileName)
        {
            LastCompileResult = results;

            if (results.Errors.Count != 0)
            {
                throw CompilerException.Create(results.Errors, options.hideCompilerWarnings);
            }
            else
            {
                if (!options.DBG) //.pdb and imported files might be needed for the debugger
                {
                    parser.DeleteImportedFiles();
                    string pdbFile = Path.Combine(Path.GetDirectoryName(assemblyFileName), Path.GetFileNameWithoutExtension(assemblyFileName) + ".pdb");
                    if (File.Exists(pdbFile))
                        File.Delete(pdbFile);
                }

                if (options.useCompiled)
                {
                    if (options.useSmartCaching)
                    {
                        MetaDataItems depInfo = new MetaDataItems();

                        //save imported scripts info
                        depInfo.AddItems(parser.ImportedFiles, false, Utils.RemoveDuplicates(options.searchDirs));

                        //save referenced local assemblies info
                        string[] newProbingDirs = depInfo.AddItems(compilerParams.ReferencedAssemblies, true, options.searchDirs);
                        foreach (string dir in newProbingDirs)
                            options.AddSearchDir(dir); //needed to be added at Compilation for further resolving during the Invoking stage

                        depInfo.StampFile(assemblyFileName);
                    }

                    FileInfo scriptFile = new FileInfo(scriptFileName);
                    FileInfo asmFile = new FileInfo(assemblyFileName);

                    if (scriptFile != null && asmFile != null)
                    {
                        asmFile.LastWriteTimeUtc = scriptFile.LastWriteTimeUtc;
                    }
                }
            }
        }

        internal CompilerResults LastCompileResult;

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern uint GetTempFileName(string lpPathName, string lpPrefixString, uint uUnique, [Out] StringBuilder lpTempFileName);

        /// <summary>
        /// Returns the name of the temporary file in the CSSCRIPT subfolder of Path.GetTempPath().
        /// </summary>
        /// <returns>Temporary file name.</returns>
        static public string GetScriptTempFile()
        {
            lock (typeof(CSExecutor))
            {
                return Path.Combine(GetScriptTempDir(), Guid.NewGuid().ToString() + ".tmp");
            }
        }

        /// <summary>
        /// Returns the name of the temporary folder in the CSSCRIPT subfolder of Path.GetTempPath().
        /// </summary>
        /// <returns>Temporary directoiry name.</returns>
        static public string GetScriptTempDir()
        {
            string dir = Path.Combine(Path.GetTempPath(), "CSSCRIPT");
            if (!Directory.Exists(dir))
                Directory.CreateDirectory(dir);
            return dir;
        }

        /// <summary>
        /// Generates the name of the cache directory for the specified script file.
        /// </summary>
        /// <param name="file">Script file name.</param>
        /// <returns>Cache directory name.</returns>
        public static string GetCacheDirectory(string file)
        {
            string commonCacheDir = Path.Combine(CSExecutor.GetScriptTempDir(), "Cache");
            string cacheDir = Path.Combine(commonCacheDir, Path.GetDirectoryName(Path.GetFullPath(file))
                                                                                .ToLower()
                                                                                .GetHashCode()
                                                                                .ToString());

            if (!Directory.Exists(cacheDir))
                Directory.CreateDirectory(cacheDir);

            return cacheDir;
        }

        ///<summary>
        /// Contains the name of the temporary cache folder in the CSSCRIPT subfolder of Path.GetTempPath(). The cache folder is specifig for every script file.
        /// </summary>
        static public string ScriptCacheDir
        {
            get
            {
                return cacheDir;
            }
        }

        /// <summary>
        /// Generates the name of the temporary cache folder in the CSSCRIPT subfolder of Path.GetTempPath(). The cache folder is specifig for every script file.
        /// </summary>
        /// <param name="scriptFile">script file</param>
        static public void SetScriptCacheDir(string scriptFile)
        {
            string dir = Path.Combine(GetScriptTempDir(), @"Cache\" + Path.GetDirectoryName(Path.GetFullPath(scriptFile)).ToLower().GetHashCode().ToString());
            if (!Directory.Exists(dir))
            {
                Directory.CreateDirectory(dir);
                using (StreamWriter sw = new StreamWriter(Path.Combine(dir, "css_info.txt")))
                    sw.Write(Environment.Version.ToString() + "\n" + Path.GetDirectoryName(Path.GetFullPath(scriptFile)) + "\n");
            }

            cacheDir = dir;
        }

        private static string cacheDir = "";

        /// <summary>
        /// Prints Help info.
        /// </summary>
        public void ShowHelp()
        {
            print(HelpProvider.BuildCommandInterfaceHelp());
        }

        /// <summary>
        /// Show sample C# scrip file.
        /// </summary>
        public void ShowSample()
        {
            print(HelpProvider.BuildSampleCode());
        }

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool SetEnvironmentVariable(string lpName, string lpValue);

        #endregion Class methods...
    }
}
